package com.sz.app.supplier.web.api;

/**
 * Function: TODO: ADD FUNCTION <br>
 * Author: Johnboy <br>
 * Date: 2017-10-13 14:06:00
 */

import com.sz.biz.app.dto.LoginDataDto;
import com.sz.biz.app.dto.TokenDto;
import com.sz.biz.app.web.security.OnlineSessionManager;
import com.sz.biz.common.sup.entity.SupUserLoginInfo;
import com.sz.biz.common.sup.service.SupUserLoginInfoService;
import com.sz.common.base.constants.UserActions;
import com.sz.common.base.dto.ResultDto;
import com.sz.common.base.logger.Logger;
import com.sz.common.core.service.Principal;
import com.sz.common.core.service.PrincipalLogUtils;
import com.sz.common.core.service.PrincipalUtils;
import com.sz.app.supplier.constants.ModuleNames;
import com.sz.app.supplier.exception.ErrorCodes;
import io.swagger.annotations.Api;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/v1/auth")
@Api(description = " ", tags = "000-0、安全登录")
public class SecurityController {
    public static final String UNKNOWN = "unknown";
    @Autowired
    private SupUserLoginInfoService supUserLoginInfoService;

    @Autowired
    private OnlineSessionManager onlineSessionManager;

    protected Logger logger = Logger.getLogger(this.getClass());

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public ResultDto login(@RequestBody LoginDataDto loginDataDto, HttpServletRequest request) throws Exception {
        String username = loginDataDto.getUserName();
        String password = loginDataDto.getPassWord();
        SupUserLoginInfo supUserLoginInfo = new SupUserLoginInfo();
        supUserLoginInfo.setUserName(username);
        supUserLoginInfo.setUserCode(username);
        supUserLoginInfo.setLoginTime(new Date());
        String cliIp = getIpAddr(request);
        supUserLoginInfo.setLoginIp(cliIp);
        logger.debug("-----获取用户的IP地址为：" + cliIp + "--------------");
        SecurityUtils.getSubject().getSession().setAttribute("username", username);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            logger.info("对用户[" + username + "]进行登录验证..验证开始");
            subject.login(token);
            logger.info("对用户[" + username + "]进行登录验证..验证通过");
            supUserLoginInfo.setErrCode(0);
            supUserLoginInfo.setLoginStatus(true);
            supUserLoginInfoService.updateUserLoginInfo(supUserLoginInfo);
        } catch (UnknownAccountException uae) {
            logger.warn("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");
            return ResultDto.createResult(ErrorCodes.ERROR_LOGIN_UNKNOWN_ACCOUNT);
        } catch (IncorrectCredentialsException ice) {
            logger.warn("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");
            supUserLoginInfo.setErrCode(ErrorCodes.ERROR_LOGIN_WRONG_PASSWORD.getCode());
            supUserLoginInfo.setLoginStatus(false);
            supUserLoginInfoService.updateUserLoginInfo(supUserLoginInfo);
            // 统计密码输入错误后剩余的输入机会次数给客户端
            int leftInputCount = (int) SecurityUtils.getSubject().getSession().getAttribute("leftInputCount");
            ResultDto resultDto = ResultDto.createResult(ErrorCodes.ERROR_LOGIN_EXCESSIVE_ATTEMPTS_LEFT_INPUT_COUNT, leftInputCount);
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("leftInputCount", leftInputCount);
            resultDto.setData(dataMap);
            return resultDto;
        } catch (LockedAccountException lae) {
            logger.warn("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");

            supUserLoginInfo.setErrCode(ErrorCodes.ERROR_LOGIN_ACCOUNT_LOCKED.getCode());
            supUserLoginInfo.setLoginStatus(false);
            supUserLoginInfoService.updateUserLoginInfo(supUserLoginInfo);
            return ResultDto.createResult(ErrorCodes.ERROR_LOGIN_ACCOUNT_LOCKED);
        } catch (ExcessiveAttemptsException eae) {
            logger.warn("对用户[" + username + "]进行登录验证..验证未通过,错误次数过多");
            return ResultDto.createResult(ErrorCodes.ERROR_LOGIN_EXCESSIVE_ATTEMPTS);
        } catch (AuthenticationException ae) {
            logger.warn("对用户[" + username + "]进行登录验证..验证未通过,堆栈轨迹如下" + ae.toString());
            return ResultDto.createResult(ErrorCodes.ERROR_LOGIN_USERNAME_PASSWORD_ERR);
        }
        TokenDto tokenDto = null;
        if (subject.isAuthenticated()) {
            logger.info("用户[" + username + "]登录认证通过");
            Principal principal = PrincipalUtils.getPrincipal();
            String ipAddress = getIpAddr(request);
            principal.setIpAddr(ipAddress);
            PrincipalLogUtils.addOperationLog(ModuleNames.SUP, ModuleNames.SUPPLIER_LOGIN, UserActions.LOGIN,
                    username + "登陆成功");
            putSessionToCache();
            tokenDto = new TokenDto(principal.getAccountName(), subject.getSession().getId().toString(),
                    subject.getSession().getTimeout());
        } else {
            token.clear();
        }
        ResultDto result = new ResultDto();
        result.setData(tokenDto);
        return result;
    }

    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public ResultDto logout(HttpServletRequest request) throws Exception {
        ResultDto dto = new ResultDto();
        if (SecurityUtils.getSubject().isAuthenticated()) { // 仅退出已登录用户
            String username = (String) SecurityUtils.getSubject().getPrincipal();// getSession().getAttribute("username");
            // 及时将用户注销消息入库
            SupUserLoginInfo supUserLoginInfo = new SupUserLoginInfo();
            supUserLoginInfo.setUserName(username);
            supUserLoginInfo.setUserCode(username);
            String cliIp = getIpAddr(request);
            supUserLoginInfo.setLoginIp(cliIp);
            supUserLoginInfo.setLoginStatus(false);
            supUserLoginInfo.setErrCode(0);
            supUserLoginInfo.setLoginTime(new Date());
            supUserLoginInfoService.updateUserLoginInfo(supUserLoginInfo);
            PrincipalLogUtils.addOperationLog(ModuleNames.SUPPLIER, ModuleNames.SUPPLIER_LOGIN, UserActions.LOGOUT,
                    username + "注销");
            // 进行用户的退出，给出提示信息
            SecurityUtils.getSubject().logout();
            dto.setData("您已安全退出");
        }
        return dto;
    }

    /**
     * 检查是否存在同一个账户多点登陆(踢出先登陆者)
     */
    private void putSessionToCache() {
        Principal principal = PrincipalUtils.getPrincipal();
        Serializable sessionId = SecurityUtils.getSubject().getSession().getId();
        String username = principal.getAccountName();
        onlineSessionManager.addOnlineSession(username, sessionId);
    }

    public static String getIpAddr(HttpServletRequest request) throws Exception {
        String ip = request.getHeader("X-Real-IP-Sup-Node");
        if (!StringUtils.isBlank(ip) && !UNKNOWN.equalsIgnoreCase(ip)) {
            return ip;
        }
        ip = request.getHeader("X-Real-IP");
        if (!StringUtils.isBlank(ip) && !UNKNOWN.equalsIgnoreCase(ip)) {
            return ip;
        }

        ip = request.getHeader("X-Forwarded-For");
        if (!StringUtils.isBlank(ip) && !UNKNOWN.equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个IP值，第一个为真实IP。
            int index = ip.indexOf(',');
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        } else {
            return request.getRemoteAddr();
        }
    }
}
